User Guide

Pre-Alpha Release

This version of JSxCAD is pre-alpha.

Some things are broken and some things will break.

You have been warned. :)

Main Site

Discussion Forum

Reponsitory


Introduction

The initial app can be opened via:

https://jsxcad.js.org/preAlpha

Appending #project will select a project (stored within local storage):

https://jsxcad.js.org/preAlpha#circle

Appending @gistUrl will load the initial script from a file named 'script' in the provided gist url:

https://jsxcad.js.org/preAlpha#circle@https://api.github.com/gists/3c39d513e91278681eed2eea27b0e589

Replace 3c39d513e91278681eed2eea27b0e589 in the example with your gist id.

Saving the project to gist is not yet supported.


Language

A small compiler is included which will transform input to a canonical form.

The basic api is implicitly imported into the top level scope, making 'circle', etc, available.

If no 'main' export is provided the compiler collects top level expressions other than exports, and bundles them into an implicit main function. An implicit 'return' is placed before the last expression.

All functions are made async, and all function calls have an implicit await.

The intent is that it should be possible to write simple things easily, and have these translated into es6 modules automatically, while also allowing es6 modules to be written out explicitly.

In some cases the analysis is confused by missing semicolons. If a script does not work, make sure all statements are terminated with semicolons.

Returning a shape from the script should produce a preview.

In order to generate stl, etc, use an operator like writeStl as described below. This should open a new window with a download button.


Operator Guide

Above

Moves the shape so that it is just above another shape (defaulting to the origin).

assemble(cube(10).above(),
         cylinder(2, 15).rotateY(90))
cube(10).above(sphere(5))

Arc Cosine

Gives the arc cosine converted to degrees.

acos(a) => Math.acos(a) / (Math.PI * 2) * 360;

acos(0) = 90
acos(0.5) = 60
acos(1) = 0

Assemble

Produces an assembly of shapes that can be manipulated as a single shape.

assemble(circle(20).translate([0, 0, -12]),
         square(40).translate([0, 0, 16]).outline(),
         cylinder(10, 20));

Components of the assembly can be extracted by tag filtering.

Components later in the assembly project holes into components earlier in the assembly so that the geometries are disjoint.

assemble(cube(30).above().as('cube'),
         cylinder(10, 40).above().as('cylinder'))
assemble(cube(30).above().as('cube'),
         cylinder(10, 40).above().as('cylinder'))
  .keep('cube')
assemble(cube(30).above().as('cube'),
         assemble(circle(40),
                  circle(50).outline()).as('circles'))
  .keep('circles')
assemble(cube(30).above().as('cube'),
         assemble(circle(40).as('circle'),
                  circle(50).outline().as('outline')))
  .drop('outline')

Back

Moves the shape so that it is just before the origin.

assemble(cylinder(2, 15).translate([0, 0, 2.5]),
         cube(10).back())
cube(10).back(sphere(5))

Below

Moves the shape so that it is just below the origin.

assemble(cylinder(2, 15).rotateY(90),
         cube(10).below())
cube(10).below(sphere(5))

Center

Moves the shape so that it is centered on the origin.

cube({ corner1: [30, -30, 10],
       corner2: [10, -10, 0] })
cube({ corner1: [30, -30, 10],
       corner2: [10, -10, 0] })
  .center()

Chain Hull

Builds a convex hull between adjacent pairs in a sequence of shapes.

chainHull(cube(3).translate([-5, 5]),
          sphere(3).translate([5, -5]),
          cylinder(3, 10).translate([-10, -10]))
  .translate([10, 10])
chainHull(circle(20).translate([0, 0, -10]),
          circle(10),
          circle(20).translate([0, 0, 10]))

Circle (disc)

Circles are approximated as surfaces delimeted by regular polygons.

Properly speaking what is produced here are discs. The circle perimeter can be extracted via outline().

circle()
circle(10)
circle({ radius: 10,
         sides: 8 })
circle({ apothem: 10,
         sides: 8 })
assemble(circle({ apothem: 10, sides: 5 }),
         circle({ radius: 10, sides: 5 }).drop(),
         circle({ radius: 10 }).outline())
circle({ diameter: 20,
         sides: 16 })

Cosine

Gives the cosine in degrees.

cos(a) => Math.cos(a / 360 * Math.PI * 2);

cos(0) = 1
cos(45) = 0.707
cos(90) = 0

Cube (cuboid)

Generates cuboids.

cube()
cube(10)
cube(10, 20, 30)
cube({ radius: 8 })
cube({ diameter: 16 })
cube({ corner1: [0, 0, 0],
       corner2: [10, 10, 10] })

Cut

Cuts a solid into two halves at z = 0, and returns each.

const [top, bottom] = cube(10).cut();
assemble(top.translate(0, 0, 1),
         bottom.translate(0, 0, -1));
const [top, bottom] = sphere(10).cut();
assemble(top.translate(0, 0, 2),
         bottom.translate(0, 0, -2));
const [top, bottom] = sphere(10).rotateY(1).cut();
assemble(top.translate(0, 0, 2),
         bottom.translate(0, 0, -2));

Cursor

A cursor is moved by transformations rather than the universe around it.

cursor()
  .translate(5)
  .turn(45)
  .translate(5)
  .interior()
cursor()
  .translate(5)
  .turn(-45)
  .translate(5)
  .interior()
cursor()
  .translate(5)
  .corner(45)
  .translate(5)
  .interior()
cursor()
  .translate(5)
  .corner(-45)
  .translate(5)
  .interior()

Cylinder

Generates cylinders.

cylinder()
cylinder(10, 2)
cylinder({ radius: 2,
           height: 10,
           resolution: 8 })
cylinder({ diameter: 6,
           height: 8,
           resolution: 16 })

Difference

Difference produces a version of the first shape with the remaining shapes removed, where applicable. Different kinds of shapes do not interact. e.g., you cannot subtract a surface from a solid.

difference(cube(10).below(),
           cube(5).below())
difference(circle(10),
           circle(2.5))
difference(assemble(cube().below(),
                    cube().above()),
           cube().right())

Drop from assembly

Generates an assembly from components in an assembly without a tag.

If no tag is supplied, the whole shape is dropped.

assemble(circle(10).as('A'),
         square(10).as('B'))
assemble(circle(10).as('A'),
         square(10).as('B'))
  .drop('A')
assemble(circle(10).as('A'),
         square(10).as('B'))
  .drop('B')
assemble(circle(10).as('A'),
         square(10).as('B'))
  .drop('A', 'B')
assemble(cube(10).below(),
         cube(8).below().drop())

Front

Moves the shape so that it is just before the origin.

assemble(cylinder(2, 15).translate([0, 0, 2.5]),
         cube(10).front())
cube(10).front(sphere(5))

Fuse

Fuse produces a simple version of a shape. All substructure is discarded.

assemble(sphere(10), sphere(10).translate(2).drop()).fuse()

Extrude

Generates a solid from a surface.

difference(circle(10),
           circle(8))
difference(circle(10),
           circle(8))
  .extrude({ height: 10 })

Hull

Builds the convex hull of a set of shapes.

hull(point([0, 0, 10]),
     circle(10))
assemble(point([0, 0, 10]),
         circle(10))
  .hull()
point([0, 0, 10]).hull(circle(10))
hull(circle(4),
     circle(2).translate(8));

Interior

Generates a surface from the interior of a simple closed path.

circle(10)
circle(10)
  .outline()
circle(10)
  .outline()
  .interior()

Intersection

Intersection produces a version of the first shape retaining only the parts included in the remaining shapes.

Different kinds of shapes do not interact. e.g., you cannot intersect a surface and a solid.

intersection(cube(12),
             sphere(8))
intersection(circle(10).translate(-5),
             circle(10).translate(5))
intersection(assemble(cube().below(),
                      cube().above()),
             sphere(1))
assemble(difference(square(10),
                    square(7))
           .translate(-2, -2),
         difference(square(10),
                    square(7))
           .translate(2, 2));
intersection(difference(square(10),
                        square(7))
               .translate(-2, -2),
             difference(square(10),
                        square(7))
               .translate(2, 2));

Keep in assembly

Generates an assembly from components in an assembly with a tag.

assemble(circle(10).as('A'),
         square(10).as('B'))
assemble(circle(10).as('A'),
         square(10).as('B'))
  .keep('A')
assemble(circle(10).as('A'),
         square(10).as('B'))
  .keep('B')
assemble(circle(10).as('A'),
         square(10).as('B'))
  .keep('A', 'B')

Left

Moves the shape so that it is just to the left of the origin.

assemble(cube(10).left(),
         cylinder(2, 15))
cube(10).left(sphere(5))

Lego

lego.stud()
lego.socket()
lego.studSheet()
lego.socketSheet()

FIX: Does not drop deep 'void'.

assemble(cube(8, 8, 3.2).above().as('plate'),
         lego.socket().above().as('socket'))

Log

Writes a string to the console.

log("Hello, World")

Max

Produces the maximum of a series of numbers.

max(1, 2, 3, 4) == 4

Measure Bounding Box

Provides the corners of the smallest orthogonal box containing the shape.

sphere(7)
const [corner1, corner2] = sphere(7).measureBoundingBox();
cube({ corner1, corner2 })

Micro Gear Motor

microGearMotor()

Minkowski (convex)

Generates the minkowski sum of a two convex shapes.

minkowski(cube(10),
          sphere(3));

Outline

Generates the outline of a surface.

difference(circle(10),
           circle(2).translate([-4]),
           circle(2).translate([4]))
difference(circle(10),
           circle(2).translate([-4]),
           circle(2).translate([4]))
  .outline()

Point

Generates a point, by default at the origin.

Note: The points are not visible in the illustrations below.

point()
point(1)
point(1, 2)
point(1, 2, 3)
point([1, 1, 0])
point([1])
point([1, 2])
point([1, 2, 3])

Points

Generates point cloud.

Note: The points are not visible in the illustrations below.

points([ -0.5, -0.5, -0.5 ],
       [ -0.5, -0.5, 0.5 ],
       [ -0.5, 0.5, -0.5 ],
       [ -0.5, 0.5, 0.5 ],
       [ 0.5, -0.5, -0.5 ],
       [ 0.5, -0.5, 0.5 ],
       [ 0.5, 0.5, -0.5 ],
       [ 0.5, 0.5, 0.5 ])
hull(points([ -0.5, -0.5, -0.5 ],
            [ -0.5, -0.5, 0.5 ],
            [ -0.5, 0.5, -0.5 ],
            [ -0.5, 0.5, 0.5 ],
            [ 0.5, -0.5, -0.5 ],
            [ 0.5, -0.5, 0.5 ],
            [ 0.5, 0.5, -0.5 ],
            [ 0.5, 0.5, 0.5 ]))

Polygon

polygon([0, 1],
        [1, 1],
        [1, 0],
        [0.2, 0.2])
polygon({ edge: 10, sides: 6 })
assemble(
  polygon({ apothem: 10, sides: 5 }),
  circle(10).drop())
assemble(
  circle(10),
  polygon({ radius: 10, sides: 5 }).drop())
polygon({ diameter: 20, sides: 3 })

Polyhedron

polyhedron({ points: [[10, 10, 0], [10, -10, 0], [-10, -10, 0], [-10, 10, 0], [0, 0, 10]],
             triangles: [[4, 1, 0], [4, 2, 1], [4, 3, 2], [4, 0, 3], [3, 0, 1], [3, 1, 2]] })

Read Data Stitch Tajima

readDst({ path: 'dst/atg-sft003.dst',
        sources: [{ file: 'dst/atg-sft003.dst' },
                  { url: 'https://jsxcad.js.org/dst/atg-sft003.dst' }] })
readDst({ path: 'dst/atg-sft003.dst',
        sources: [{ file: 'dst/atg-sft003.dst' },
                  { url: 'https://jsxcad.js.org/dst/atg-sft003.dst' }] })

Read Font

readFont reads in a font and produces a function that renders text as a surface with that font.

The rendering function takes an option defaulting to { emSize = 10 } and a string of text. This means that one M is 10 mm in height.

const greatVibes = readFont({ path: 'font/great-vibes/GreatVibes-Regular.ttf' });
greatVibes({ emSize: 20 }, "M").extrude(5).rotateX(90).above().center()
const greatVibes = readFont({ path: 'font/great-vibes/GreatVibes-Regular.ttf' });
greatVibes({ emSize: 10 }, "M").center()
const greatVibes = readFont({ path: 'font/great-vibes/GreatVibes-Regular.ttf' });
greatVibes({ emSize: 20 }, "M").center()
const greatVibes = readFont({ path: 'font/great-vibes/GreatVibes-Regular.ttf' });
greatVibes({ emSize: 16 }, "CA").center()

Read LDraw Parts

readLDraw({ part: '3004.dat' })

Read Shape Geometry

This reads tagged geometry in json format and produces a shape.

writeShape({ path: 'geometry/cube' }, cube())
readShape({ path: 'geometry/cube' })

A shape building function can be supplied to generate the shape to read if absent.

The second read will not call the build function, and it will be present in re-runs.

This allows the caching of complex geometry for fast recomposition.

readShape({ path: 'geometry/sphere' }, () => sphere())
readShape({ path: 'geometry/sphere' }, () => sphere())

Read STL

readStl({ path: 'stl/teapot.stl',
          format: 'ascii',
          sources: [{ file: 'stl/teapot.stl' },
                    { url: 'https://jsxcad.js.org/stl/teapot.stl' }] })

Read Scalable Vector Format


const svg = readSvg({ path: 'svg/butterfly.svg',
                      sources: [{ file: 'svg/butterfly.svg' },
                                { url: 'https://jsxcad.js.org/svg/butterfly.svg' }] });
svg.center().scale(0.02)

Right

Moves the shape so that it is just to the right of the origin.

assemble(cube(10).right(),
         cylinder(2, 15))
cube(10).right(sphere(5))

Rotate X

Rotates the shape around the X axis.

square(10)
square(10).rotateX(90)

Rotate Y

Rotates the shape around the Y axis.

square(10)
square(10).rotateY(90)

Rotate Z

Rotates the shape around the Z axis.

square(10)
square(10).rotateZ(45)

Scale

Scales an object uniformly or per axis.

cube()
cube().scale(2)
cube().scale([1, 2, 3])

Section

Produces a cross-section of a solid as a surface.

difference(cylinder(10, 10),
           cylinder(8, 10))
difference(sphere(10),
           sphere(8))
  .section()
difference(sphere(10),
           sphere(8))
  .section()
  .outline()

Sine

Gives the sine in degrees.

sin(a) => Math.sin(a / 360 * Math.PI * 2);

sin(0) = 0
sin(45) = 0.707
sin(90) = 1

Sphere

Generates spheres.

sphere()
sphere(10)
sphere({ radius: 8, resolution: 5 })
sphere({ diameter: 16, resolution: 64 })

Square Root

Gives the the square root of a number.

sqrt(a) => Math.sqrt(a);

sqrt(0) = 0
sqrt(4) = 2
sqrt(16) = 4

Square (rectangle)

Properly speaking what is produced here are rectangles.

square()
square(10)
square(6, 12)
square({ edge: 10 })
assemble(circle(10),
         square({ radius: 10 })
           .drop())
assemble(square({ apothem: 10 }),
         circle(10).drop())
square({ diameter: 20 })

Svg Path

Generates a path from svg path data.

svgPath({},
        'M 120.25163,89.678938 C 105.26945,76.865343 86.290871,70.978848 64.320641,70.277872 z')
  .center()
  .scale(0.2)

Tetrahedron

Generates tetrahedrons.

tetrahedron()
tetrahedron(10)
tetrahedron({ radius: 8 })
tetrahedron({ diameter: 16 })

Translate

Translation moves a shape.

assemble(circle(),
         sphere().above())
assemble(circle(),
         sphere().above()
                 .translate(0, 0, 1))
assemble(circle(),
         sphere().above()
                 .translate(0, 1, 0))
assemble(circle(),
         sphere().above()
                 .translate([-1, -1, 1]))

Triangle

triangle()
triangle(20)
triangle({ radius: 10 })
assemble(circle(10),
         triangle({ radius: 10 })
           .drop())
assemble(triangle({ apothem: 5 }),
         circle(5).drop())
assemble(triangle({ radius: 10 })
           .rotateZ(180),
         triangle({ diameter: 10 })
           .drop())

Union

Union produces a version of the first shape extended to cover the remaining shapes, as applicable. Different kinds of shapes do not interact. e.g., you cannot union a surface and a solid.

union(sphere(5).left(),
      sphere(5),
      sphere(5).right())
union(sphere(5).left(),
      sphere(5),
      sphere(5).right())
  .section()
  .outline()
union(triangle(),
      triangle().rotateZ(180))
union(triangle(),
      triangle().rotateZ(180))
  .outline()
union(assemble(cube().left(),
               cube().right()),
      cube().front())
  .section()
  .outline()

Wireframe

Generates a set of paths outlining a solid.

cube(10).wireframe()
sphere(10).wireframe()

Write PDF

cube().section().writePdf('cube.pdf');
writePdf({ path: 'cube.pdf' }, cube().section());

Write Shape Geometry

This writes a shape as a tagged geometry in json format.

cube().writeShape('cube.shape');
readShape({ path: 'cube.shape' })
writeShape({ path: 'cube.shape' }, cube())
readShape({ path: 'cube.shape' })

Write STL

cube().writeStl('cube.stl');
readStl({ path: 'cube.stl' });
writeStl({ path: 'cube.stl' }, cube());
readStl({ path: 'cube.stl' });

Write SVG

cube().section().writeSvg('svg/cube1.svg');
readSvg({ path: 'svg/cube1.svg' })
writeSvg({ path: 'svg/cube2.svg' }, cube().section());
readSvg({ path: 'svg/cube2.svg' })

Write SVG Photo

This takes a scene and a camera position and generates a two-dimensional SVG representation as a svg tag.

Note: Illustrations broken due to scaling issue affecting readSvg.

cube().writeSvgPhoto({ path: 'svg/cube3.svg', view: { position: [10, 10, 10], target: [0, 0, 0] } });
readSvg({ path: 'svg/cube3.svg' })
writeSvgPhoto({ path: 'svg/cube4.svg', view: { position: [10, 10, 10], target: [0, 0, 0] } }, cube());
readSvg({ path: 'svg/cube4.svg' })